package com.gitee.weixin.mp.service;

import com.gitee.weixin.mp.domain.MpConfig;
import com.gitee.weixin.mp.domain.MpConfigRepository;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceApacheHttpClientImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.Optional;

@Service
public class MpConfigService {
    public static final int    _10_MINS = 600 * 1000;
    private static      Logger logger   = LoggerFactory.getLogger(MpConfigService.class);

    @Autowired
    MpConfigRepository repository;

    @Value("${wechat.mp.appId}")
    private String appId;

    /**
     * 获取配置信息
     *
     * @return
     */
    public MpConfig getConfig() {
        Optional<MpConfig> opt = repository.findByAppId(appId);
        if (opt.isPresent()) {
            return opt.get();
        } else {
            logger.error("公众号基本配置信息有误! 请检查配置文件application.yml以及数据库表mp_config : appId={}", appId);
            return null;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public String getAccessToken(boolean forceRefresh, WxMpServiceApacheHttpClientImpl wxMpService) throws WxErrorException {
        // 从内存获取token
        if (!wxMpService.getWxMpConfigStorage().isAccessTokenExpired()) {
            if (!forceRefresh) {
                return wxMpService.getWxMpConfigStorage().getAccessToken();
            }
        }

        // 从数据库获取AccessToken
        MpConfig mpConfig = getConfig();
        if (mpConfig != null) {
            // 如果token有效失效时间在10分钟以上则使用数据库存储的token
            long expiresInMilSeconds = mpConfig.getAccessTokenExpiresTime() - System.currentTimeMillis();
            if (StringUtils.isNotEmpty(mpConfig.getAccessToken()) && expiresInMilSeconds > _10_MINS) {
                wxMpService.getWxMpConfigStorage().updateAccessToken(mpConfig.getAccessToken(), (int) expiresInMilSeconds);
                if (!forceRefresh) {
                    return mpConfig.getAccessToken();
                }
            }

            // 对数据行加互斥锁
            repository.updateAccessTokenExpiresTimeByAppId(appId, -1);

            // 获取新的token
            if (wxMpService.getWxMpConfigStorage().isAccessTokenExpired() || forceRefresh) {
                String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL, wxMpService.getWxMpConfigStorage().getAppId(),
                                           wxMpService.getWxMpConfigStorage().getSecret());
                WxAccessToken accessToken = null;
                try {
                    HttpGet httpGet = new HttpGet(url);
                    if (wxMpService.getRequestHttpProxy() != null) {
                        RequestConfig config = RequestConfig.custom().setProxy(wxMpService.getRequestHttpProxy()).build();
                        httpGet.setConfig(config);
                    }
                    try (CloseableHttpResponse response = wxMpService.getRequestHttpClient().execute(httpGet)) {
                        String resultContent = new BasicResponseHandler().handleResponse(response);
                        WxError error = WxError.fromJson(resultContent);
                        if (error.getErrorCode() != 0) {
                            throw new WxErrorException(error);
                        }
                        accessToken = WxAccessToken.fromJson(resultContent);
                    } finally {
                        httpGet.releaseConnection();

                    }
                    if (accessToken != null) {
                        // 更新到内存
                        wxMpService.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
                        // 更新token到数据库
                        repository.updateAccessTokenByAppId(appId, accessToken.getAccessToken(), wxMpService.getWxMpConfigStorage().getExpiresTime());
                        return accessToken.getAccessToken();
                    }

                } catch (IOException e) {
                    logger.error("更新accessToken失败", e);
                    throw new RuntimeException(e);
                }
            }
        }
        return "";

    }
}
